﻿using System;

namespace Reactor.Messaging
{
    public abstract class MessagingChannel : IMessagingChannel, IEquatable<MessagingChannel>
    {
        /// <summary>
        /// The prefix to a typed queue's name.
        /// <example>Fully typed queue name contains this prefix: "queue://MyTestQueue"</example>
        /// </summary>
        public const string TypedQueuePrefix = "queue://";

        /// <summary>
        /// The prefix to a typed topic's name.
        /// <example>Fully typed topic name contains this prefix: "topic://MyTestTopic"</example>
        /// </summary>
        public const string TypedTopicPrefix = "topic://";

        #region IMessagingChannel Members

        /// <summary>
        /// Gets or sets the MessagingChannel's name.
        /// </summary>
        /// <value>The name.</value>
        public string DestinationName { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the message delivery to this MessagingChannel is to be transactional.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is transactional; otherwise, <c>false</c>.
        /// </value>
        public bool IsTransactional { get; set; }

        /// <summary>
        /// Gets typed version of the MessagingChannel name.
        /// </summary>
        /// <returns></returns>
        public abstract string GetTypedDestinationName();

        #endregion

        #region Equality Members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(MessagingChannel other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.DestinationName, DestinationName);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != GetType()) return false;
            return Equals((MessagingChannel) obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type. 
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            return (DestinationName != null ? DestinationName.GetHashCode() : 0);
        }

        #endregion

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance. 
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return DestinationName;
        }

        #region Static Members

        /// <summary>
        /// Provides a MessagingChannel instanced based on the typed MessagingChannel name provided.
        /// </summary>
        /// <param name="typedName">Name of the typed.</param>
        /// <returns></returns>
        public static IMessagingChannel FromTypedDestinationName(string typedName)
        {
            if (string.IsNullOrEmpty(typedName)) throw new ArgumentNullException("typedName");

            if (typedName.StartsWith(TypedQueuePrefix))
                return new PointToPointChannel(typedName.Replace(TypedQueuePrefix, ""));

            if (typedName.StartsWith(TypedTopicPrefix))
                return new PubSubChannel(typedName.Replace(TypedTopicPrefix, ""));

            throw new ArgumentOutOfRangeException("typedName", typedName, string.Format("Typed name must be prefixed with: '{0}' or '{1}'", TypedQueuePrefix, TypedTopicPrefix));
        }

        #endregion
    }
}
